<template>
  <div class="graph-visualizer">
    <div class="header">
      <h3>{{ title }}</h3>
      <div class="controls">
        <el-input-number v-model="vertexCount" :min="2" :max="8" size="small" label="节点数量" @change="resetGraph" />
        <el-switch v-model="isDirected" active-text="有向图" inactive-text="无向图" @change="updateGraphType" />
        <el-switch v-model="isWeighted" active-text="带权图" inactive-text="无权图" @change="updateGraphType" />
        <el-button @click="reset">
          重置
        </el-button>
      </div>
    </div>

    <div class="visualization-container">
      <div class="graph-view">
        <!-- 图形视图 -->
        <div class="vertex-container">
          <div 
            v-for="i in vertexCount" 
            :key="`vertex-${i}`"
            class="vertex"
            :class="{ 'highlight': highlightedVertices.includes(i-1) }"
            :style="{ 
              left: `${Math.cos(2 * Math.PI * (i-1) / vertexCount - Math.PI/2) * 120 + 150}px`,
              top: `${Math.sin(2 * Math.PI * (i-1) / vertexCount - Math.PI/2) * 120 + 150}px`
            }"
          >
            {{ i-1 }}
          </div>
          
          <svg width="300" height="300" class="edges">
            <!-- 为有向图添加箭头标记 -->
            <defs v-if="isDirected">
              <marker 
                id="arrowhead" 
                markerWidth="10" 
                markerHeight="7" 
                refX="9" 
                refY="3.5" 
                orient="auto"
              >
                <polygon points="0 0, 10 3.5, 0 7" fill="#409eff" />
              </marker>
            </defs>
            
            <!-- 自环用路径渲染 -->
            <path 
              v-for="(edge, index) in edges.filter(e => e.isLoop)" 
              :key="`loop-${index}`"
              :d="edge.path"
              :class="{ 'highlighted-edge': highlightedEdges.includes(edge.id) }"
              stroke="#409eff"
              stroke-width="2"
              fill="none"
              :marker-end="isDirected ? 'url(#arrowhead)' : ''"
            />
            
            <!-- 普通边用线段渲染 -->
            <line 
              v-for="(edge, index) in edges.filter(e => !e.isLoop)" 
              :key="`edge-${index}`"
              :x1="edge.x1" 
              :y1="edge.y1" 
              :x2="edge.x2" 
              :y2="edge.y2"
              :class="{ 'highlighted-edge': highlightedEdges.includes(edge.id) }"
              stroke="#409eff"
              stroke-width="2"
              :marker-end="isDirected ? 'url(#arrowhead)' : ''"
            />
            
            <!-- 边权重 -->
            <text 
              v-if="isWeighted"
              v-for="(edge, index) in edges" 
              :key="`weight-${index}`"
              :x="edge.isLoop ? edge.x1 + 30 : (edge.x1 + edge.x2) / 2 + 5" 
              :y="edge.isLoop ? edge.y1 - 20 : (edge.y1 + edge.y2) / 2 - 5"
              fill="#f56c6c"
              font-size="14"
            >
              {{ edge.weight }}
            </text>
          </svg>
        </div>
        
        <!-- 邻接矩阵视图 -->
        <div v-if="type === 'adjacencyMatrix'" class="matrix-container">
          <table class="matrix-table">
            <thead>
              <tr>
                <th></th>
                <th v-for="i in vertexCount" :key="`head-${i}`">{{ i-1 }}</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="i in vertexCount" :key="`row-${i}`">
                <th>{{ i-1 }}</th>
                <td 
                  v-for="j in vertexCount" 
                  :key="`cell-${i}-${j}`"
                  :class="{ 
                    'highlight': highlightedCells.includes(`${i-1}-${j-1}`),
                    'diagonal': i-1 === j-1 && !isDirected
                  }"
                  @click="toggleEdge(i-1, j-1)"
                >
                  {{ matrix[i-1][j-1] || 0 }}
                </td>
              </tr>
            </tbody>
          </table>
          <div class="help-text">点击单元格添加/删除边</div>
        </div>
        
        <!-- 邻接表视图 -->
        <div v-if="type === 'adjacencyList'" class="adj-list-container">
          <div 
            v-for="(list, vertexIndex) in adjList" 
            :key="`list-${vertexIndex}`" 
            class="adj-list-row"
            :class="{ 'highlight-row': highlightedVertices.includes(vertexIndex) }"
          >
            <div class="vertex-label">{{ vertexIndex }} →</div>
            <div class="adj-vertices">
              <div 
                v-for="(edge, edgeIndex) in list" 
                :key="`edge-${vertexIndex}-${edgeIndex}`"
                class="adj-vertex"
                :class="{ 'highlight': highlightedEdges.includes(getEdgeId(vertexIndex, edge.vertex)) }"
                @click="toggleAdjListEdge(vertexIndex, edge.vertex)"
              >
                {{ edge.vertex }}{{ isWeighted ? ` (${edge.weight})` : '' }}
              </div>
              <el-button 
                v-if="list.length === 0" 
                size="small" 
                @click="showAddEdgeDialog(vertexIndex)"
                class="add-edge-btn"
              >
                添加
              </el-button>
            </div>
          </div>
          <div class="help-text">点击节点添加/删除边</div>
        </div>
      </div>
    </div>
    
    <!-- 添加边对话框 -->
    <el-dialog v-model="addEdgeDialogVisible" title="添加边" width="30%" append-to-body>
      <el-form :model="newEdge" label-width="80px">
        <el-form-item label="目标节点">
          <el-select v-model="newEdge.to" placeholder="选择目标节点">
            <el-option 
              v-for="i in vertexCount" 
              :key="i-1" 
              :label="`节点 ${i-1}`" 
              :value="i-1"
              :disabled="!isDirected && newEdge.from === i-1"
            />
          </el-select>
        </el-form-item>
        <el-form-item v-if="isWeighted" label="权重">
          <el-input-number v-model="newEdge.weight" :min="1" :max="9" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="addEdgeDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="addEdge">确认</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, watch, nextTick } from 'vue'
import { ElMessage } from 'element-plus'

const props = defineProps({
  type: {
    type: String,
    required: true,
    validator: (value) => ['adjacencyMatrix', 'adjacencyList'].includes(value)
  },
  maxVertices: {
    type: Number,
    default: 8
  }
})

// 图的基本属性
const vertexCount = ref(5)
const isDirected = ref(false)
const isWeighted = ref(true)
const matrix = ref([])
const adjList = ref([])
const edges = ref([])

// 高亮状态
const highlightedVertices = ref([])
const highlightedEdges = ref([])
const highlightedCells = ref([])

// 添加边对话框
const addEdgeDialogVisible = ref(false)
const newEdge = ref({
  from: 0,
  to: 0,
  weight: 1
})

// 计算属性
const title = computed(() => {
  return props.type === 'adjacencyMatrix' ? '邻接矩阵可视化' : '邻接表可视化'
})

// 重置图
const resetGraph = () => {
  if (props.type === 'adjacencyMatrix') {
    initMatrix()
  } else {
    initAdjList()
  }
}

// 初始化邻接矩阵
const initMatrix = () => {
  matrix.value = Array(vertexCount.value).fill().map(() => Array(vertexCount.value).fill(0))
  highlightedVertices.value = []
  highlightedEdges.value = []
  highlightedCells.value = []
  updateGraphView()
}

// 初始化邻接表
const initAdjList = () => {
  adjList.value = Array(vertexCount.value).fill().map(() => [])
  highlightedVertices.value = []
  highlightedEdges.value = []
  updateGraphView()
}

// 更新图的类型（有向/无向，带权/无权）
const updateGraphType = () => {
  if (props.type === 'adjacencyMatrix') {
    updateMatrixType()
  } else {
    updateListType()
  }
}

// 更新邻接矩阵的类型
const updateMatrixType = () => {
  // 如果从有向图切换到无向图，确保矩阵是对称的
  if (!isDirected.value) {
    for (let i = 0; i < vertexCount.value; i++) {
      for (let j = i + 1; j < vertexCount.value; j++) {
        if (matrix.value[i][j] !== matrix.value[j][i]) {
          const maxWeight = Math.max(matrix.value[i][j], matrix.value[j][i])
          matrix.value[i][j] = maxWeight
          matrix.value[j][i] = maxWeight
        }
      }
      // 删除自环
      matrix.value[i][i] = 0
    }
  }
  
  // 如果切换为无权图，所有非零值变为1
  if (!isWeighted.value) {
    for (let i = 0; i < vertexCount.value; i++) {
      for (let j = 0; j < vertexCount.value; j++) {
        if (matrix.value[i][j] !== 0) {
          matrix.value[i][j] = 1
        }
      }
    }
  }
  
  updateGraphView()
}

// 更新邻接表的类型
const updateListType = () => {
  // 将邻接表转换为邻接矩阵来处理
  const tempMatrix = Array(vertexCount.value).fill().map(() => Array(vertexCount.value).fill(0))
  
  for (let i = 0; i < adjList.value.length; i++) {
    for (let edge of adjList.value[i]) {
      tempMatrix[i][edge.vertex] = edge.weight
    }
  }
  
  // 使用邻接矩阵的更新逻辑
  if (!isDirected.value) {
    for (let i = 0; i < vertexCount.value; i++) {
      for (let j = i + 1; j < vertexCount.value; j++) {
        if (tempMatrix[i][j] !== tempMatrix[j][i]) {
          const maxWeight = Math.max(tempMatrix[i][j], tempMatrix[j][i])
          tempMatrix[i][j] = maxWeight
          tempMatrix[j][i] = maxWeight
        }
      }
      // 删除自环
      tempMatrix[i][i] = 0
    }
  }
  
  if (!isWeighted.value) {
    for (let i = 0; i < vertexCount.value; i++) {
      for (let j = 0; j < vertexCount.value; j++) {
        if (tempMatrix[i][j] !== 0) {
          tempMatrix[i][j] = 1
        }
      }
    }
  }
  
  // 将矩阵转回邻接表
  adjList.value = []
  for (let i = 0; i < vertexCount.value; i++) {
    const neighbors = []
    for (let j = 0; j < vertexCount.value; j++) {
      if (tempMatrix[i][j] !== 0) {
        neighbors.push({ vertex: j, weight: tempMatrix[i][j] })
      }
    }
    adjList.value.push(neighbors)
  }
  
  updateGraphView()
}

// 切换邻接矩阵中的边
const toggleEdge = (from, to) => {
  // 检查是否为无向图的自环
  if (!isDirected.value && from === to) {
    ElMessage.warning('无向图不支持自环')
    return
  }
  
  if (matrix.value[from][to] === 0) {
    // 添加边
    const weight = isWeighted.value ? Math.floor(Math.random() * 9) + 1 : 1
    matrix.value[from][to] = weight
    
    // 如果是无向图，同时更新对称位置
    if (!isDirected.value && from !== to) {
      matrix.value[to][from] = weight
    }
    
    // 高亮新添加的边
    highlightedCells.value = [`${from}-${to}`]
    if (!isDirected.value && from !== to) {
      highlightedCells.value.push(`${to}-${from}`)
    }
  } else {
    // 删除边
    matrix.value[from][to] = 0
    
    // 如果是无向图，同时更新对称位置
    if (!isDirected.value && from !== to) {
      matrix.value[to][from] = 0
    }
  }
  
  updateGraphView()
  
  // 短暂高亮，然后取消
  setTimeout(() => {
    highlightedCells.value = []
    highlightedVertices.value = []
    highlightedEdges.value = []
  }, 1000)
}

// 切换邻接表中的边
const toggleAdjListEdge = (from, to) => {
  const edgeIndex = adjList.value[from].findIndex(edge => edge.vertex === to)
  
  if (edgeIndex === -1) {
    // 添加边
    const weight = isWeighted.value ? Math.floor(Math.random() * 9) + 1 : 1
    adjList.value[from].push({ vertex: to, weight })
    
    // 如果是无向图，同时更新对称位置
    if (!isDirected.value && from !== to) {
      adjList.value[to].push({ vertex: from, weight })
    }
    
    // 高亮添加的边
    highlightedVertices.value = [from, to]
    highlightedEdges.value = [getEdgeId(from, to)]
  } else {
    // 删除边
    adjList.value[from].splice(edgeIndex, 1)
    
    // 如果是无向图，同时更新对称位置
    if (!isDirected.value && from !== to) {
      const reverseEdgeIndex = adjList.value[to].findIndex(edge => edge.vertex === from)
      if (reverseEdgeIndex !== -1) {
        adjList.value[to].splice(reverseEdgeIndex, 1)
      }
    }
  }
  
  updateGraphView()
  
  // 短暂高亮，然后取消
  setTimeout(() => {
    highlightedVertices.value = []
    highlightedEdges.value = []
  }, 1000)
}

// 显示添加边对话框
const showAddEdgeDialog = (fromVertex) => {
  newEdge.value = {
    from: fromVertex,
    to: 0,
    weight: isWeighted.value ? Math.floor(Math.random() * 9) + 1 : 1
  }
  addEdgeDialogVisible.value = true
}

// 添加边
const addEdge = () => {
  if (props.type === 'adjacencyMatrix') {
    toggleEdge(newEdge.value.from, newEdge.value.to)
  } else {
    toggleAdjListEdge(newEdge.value.from, newEdge.value.to)
  }
  addEdgeDialogVisible.value = false
}

// 获取边的唯一ID
const getEdgeId = (from, to) => {
  return `${from}-${to}`
}

// 从邻接表中获取边的ID
const getEdgeIndexFromAdjList = (from, to) => {
  return getEdgeId(from, to)
}

// 重置
const reset = () => {
  resetGraph()
}

// 更新图的可视化
const updateGraphView = () => {
  edges.value = []
  
  if (props.type === 'adjacencyMatrix') {
    // 从邻接矩阵生成边
    for (let i = 0; i < vertexCount.value; i++) {
      for (let j = 0; j < vertexCount.value; j++) {
        if (matrix.value[i][j] !== 0) {
          // 计算节点位置，从顶部开始顺时针分布
          const angle1 = 2 * Math.PI * i / vertexCount.value - Math.PI/2
          const angle2 = 2 * Math.PI * j / vertexCount.value - Math.PI/2
          
          const x1 = Math.cos(angle1) * 120 + 150
          const y1 = Math.sin(angle1) * 120 + 150
          const x2 = Math.cos(angle2) * 120 + 150
          const y2 = Math.sin(angle2) * 120 + 150
          
          // 处理自环
          if (i === j) {
            const controlX = x1 + 40
            const controlY = y1 - 40
            
            edges.value.push({
              id: getEdgeId(i, j),
              from: i,
              to: j,
              weight: matrix.value[i][j],
              x1, y1, x2, y2,
              isLoop: true,
              path: `M ${x1} ${y1} C ${controlX} ${controlY}, ${controlX} ${controlY}, ${x2} ${y2}`
            })
          } else {
            // 处理无向图中的边去重
            if (!isDirected.value && i > j) continue
            
            edges.value.push({
              id: getEdgeId(i, j),
              from: i,
              to: j,
              weight: matrix.value[i][j],
              x1, y1, x2, y2,
              isLoop: false
            })
          }
        }
      }
    }
  } else if (props.type === 'adjacencyList') {
    // 从邻接表生成边
    for (let i = 0; i < adjList.value.length; i++) {
      for (let edge of adjList.value[i]) {
        // 计算节点位置
        const angle1 = 2 * Math.PI * i / vertexCount.value - Math.PI/2
        const angle2 = 2 * Math.PI * edge.vertex / vertexCount.value - Math.PI/2
        
        const x1 = Math.cos(angle1) * 120 + 150
        const y1 = Math.sin(angle1) * 120 + 150
        const x2 = Math.cos(angle2) * 120 + 150
        const y2 = Math.sin(angle2) * 120 + 150
        
        // 处理自环
        if (i === edge.vertex) {
          const controlX = x1 + 40
          const controlY = y1 - 40
          
          edges.value.push({
            id: getEdgeId(i, edge.vertex),
            from: i,
            to: edge.vertex,
            weight: edge.weight,
            x1, y1, x2, y2,
            isLoop: true,
            path: `M ${x1} ${y1} C ${controlX} ${controlY}, ${controlX} ${controlY}, ${x2} ${y2}`
          })
        } else {
          // 处理无向图中的边去重
          if (!isDirected.value) {
            const existingEdge = edges.value.find(e => 
              (e.from === edge.vertex && e.to === i) || (e.from === i && e.to === edge.vertex)
            )
            if (existingEdge) continue
          }
          
          edges.value.push({
            id: getEdgeId(i, edge.vertex),
            from: i,
            to: edge.vertex,
            weight: edge.weight,
            x1, y1, x2, y2,
            isLoop: false
          })
        }
      }
    }
  }
}

// 监听属性变化
watch(() => props.type, resetGraph, { immediate: true })

// 初始化
onMounted(() => {
  resetGraph()
})
</script>

<style scoped>
.graph-visualizer {
  padding: 20px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.controls {
  display: flex;
  gap: 15px;
  align-items: center;
}

.visualization-container {
  min-height: 400px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  padding: 20px;
}

.graph-view {
  display: flex;
  gap: 30px;
  flex-wrap: wrap;
  justify-content: center;
}

.vertex-container {
  position: relative;
  width: 300px;
  height: 300px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
}

.vertex {
  position: absolute;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background-color: #409eff;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  transform: translate(-50%, -50%);
  transition: all 0.3s;
  z-index: 2;
}

.vertex.highlight {
  background-color: #f56c6c;
  transform: translate(-50%, -50%) scale(1.2);
  z-index: 10;
}

.edges {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}

.edges line, .edges path {
  transition: all 0.3s;
}

.edges .highlighted-edge {
  stroke: #f56c6c;
  stroke-width: 3;
}

/* 邻接矩阵样式 */
.matrix-container {
  min-width: 300px;
  overflow-x: auto;
}

.matrix-table {
  border-collapse: collapse;
  width: 100%;
}

.matrix-table th, .matrix-table td {
  border: 1px solid #ebeef5;
  padding: 8px;
  text-align: center;
  transition: all 0.3s;
}

.matrix-table th {
  background-color: #f5f7fa;
  font-weight: bold;
}

.matrix-table td {
  cursor: pointer;
}

.matrix-table td:hover {
  background-color: #ecf5ff;
}

.matrix-table td.highlight {
  background-color: #f56c6c;
  color: white;
}

.matrix-table td.diagonal {
  background-color: #f5f7fa;
  cursor: not-allowed;
}

/* 邻接表样式 */
.adj-list-container {
  min-width: 300px;
  max-width: 400px;
}

.adj-list-row {
  display: flex;
  margin-bottom: 10px;
  padding: 8px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  transition: all 0.3s;
}

.adj-list-row.highlight-row {
  background-color: #ecf5ff;
  border-color: #409eff;
}

.vertex-label {
  font-weight: bold;
  margin-right: 10px;
  min-width: 40px;
}

.adj-vertices {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.adj-vertex {
  padding: 4px 8px;
  background-color: #409eff;
  color: white;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s;
}

.adj-vertex:hover {
  background-color: #337ecc;
}

.adj-vertex.highlight {
  background-color: #f56c6c;
  transform: scale(1.1);
}

.add-edge-btn {
  min-width: 80px;
}

.help-text {
  text-align: center;
  color: #909399;
  margin-top: 10px;
  font-size: 14px;
  font-style: italic;
}
</style> 